/*
 * Copyright 2013 Sigurd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package de.digiway.rapidbreeze.client.infrastructure.cnl;

import com.sun.jersey.core.util.Base64;
import de.digiway.rapidbreeze.client.infrastructure.BusEvents;
import de.digiway.rapidbreeze.client.model.collector.LinkBundle;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import javafx.application.Platform;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Response;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.ContextFactory;
import org.mozilla.javascript.Scriptable;
import org.rribbit.RRB;

/**
 *
 * @author Sigurd
 */
public class ClickAndLoadHandler extends AbstractHandler {

    private static final String POST_METHOD = "POST";
    private static final String GET_METHOD = "GET";
    private static final String STARTED_URI = "/flash/";
    private static final String STARTED_RESPONSE = "JDownloader";
    private static final String SCRIPT = "/jdcheck.js";
    private static final String SCRIPT_RESPONSE = "jdownloader=true;\nvar version='9.581';";
    private static final String ADD_LINKS_URI = "/flash/addcrypted2";

    @Override
    public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        switch (request.getMethod().toUpperCase()) {
            case GET_METHOD:
                handleGet(request, response);
                break;
            case POST_METHOD:
                handlePost(request, response);
                break;
        }
    }

    private void handleGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String requestURI = request.getRequestURI();
        try (PrintWriter writer = response.getWriter()) {
            switch (requestURI) {
                case STARTED_URI:
                    writer.write(STARTED_RESPONSE);
                    break;
                case SCRIPT:
                    writer.write(SCRIPT_RESPONSE);
                    break;
                default:
                    response.sendError(Response.SC_NOT_FOUND);
                    break;
            }
        }
    }

    private void handlePost(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String requestURI = request.getRequestURI();
        switch (requestURI) {
            case ADD_LINKS_URI:
                if (addLinks(request)) {
                    try (PrintWriter writer = response.getWriter()) {
                        writer.write("success");
                    }
                }
                break;
            default:
                response.sendError(Response.SC_NOT_FOUND);
                break;
        }
    }

    private boolean addLinks(HttpServletRequest request) throws IOException {
        String passwords = null;
        String source = null;
        String jk = null;
        String crypted = null;
        for (Map.Entry<String, String[]> entry : request.getParameterMap().entrySet()) {
            String value = entry.getValue()[0];
            switch (entry.getKey().toLowerCase()) {
                case "passwords":
                    passwords = value;
                    break;
                case "source":
                    source = value;
                    break;
                case "jk":
                    jk = value;
                    break;
                case "crypted":
                    crypted = value;
                    break;
            }
        }

        if (jk != null) {
            Context cx = null;
            try {
                cx = ContextFactory.getGlobal().enterContext();
                Scriptable scope = cx.initStandardObjects();
                String fun = jk + "  f()";
                Object result = cx.evaluateString(scope, fun, "<cmd>", 1, null);
                byte[] key = hexStringToByteArray(Context.toString(result));
                byte[] decoded = Base64.decode(crypted);
                String decryptedUrls = decrypt(decoded, key);
                String[] split = StringUtils.split(decryptedUrls, "\n");
                if (split.length > 0) {
                    List<URL> urls = new ArrayList<>();
                    for (String url : split) {
                        String trimmed = url.trim();
                        if (!trimmed.isEmpty()) {
                            urls.add(new URL(url));
                        }
                    }
                    LinkBundle lb = new LinkBundle(urls);
                    RRB.get().send(BusEvents.ADD_LINK_BUNDLE, lb);
                    Platform.runLater(new Runnable() {
                        @Override
                        public void run() {
                            RRB.get().send(BusEvents.OPEN_DOWNLOAD_COLLECTOR_WINDOW);
                        }
                    });
                    return true;
                }
            } finally {
                if (cx != null) {
                    Context.exit();
                }
            }
        }

        return false;
    }

    private String decrypt(final byte[] b, final byte[] key) {
        try {
            final Cipher cipher;
            final IvParameterSpec ivSpec = new IvParameterSpec(key);
            final SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
            cipher = Cipher.getInstance("AES/CBC/NoPadding");
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivSpec);
            return new String(cipher.doFinal(b), "UTF-8");
        } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException | UnsupportedEncodingException ex) {
            throw new IllegalStateException("Error occured during decryption.", ex);
        }
    }

    private byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                    + Character.digit(s.charAt(i + 1), 16));
        }
        return data;
    }
}
